package org.dr.st_docker.resource;

import io.micrometer.common.lang.Nullable;
import org.springframework.core.io.InputStreamSource;
import org.springframework.util.FileCopyUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;

/**
 * InputStreamSource接口，
 * 用于抽象对低级资源的访问
 * 使用该Resource中的各种方法
 */
public interface Resource extends InputStreamSource {

    /**
     * 判断资源是否物理存在。
     * 这个方法进行确切的存在性检查，而资源的存在只保证了有效的描述符。
     */
    boolean exists();

    /**
     * 指示是否可以通过 {@link #getInputStream()} 读取此资源的非空内容。
     * 通常对于存在的典型资源描述符，返回 {@code true}，因为它严格隐含了 {@link #exists()} 的语义。
     * 注意，实际的内容读取可能仍然会失败。然而，{@code false} 明确指出资源内容无法读取。
     */
    default boolean isReadable() {
        return exists();
    }

    /**
     * 指示此资源是否代表一个开放的流句柄。
     * 如果为 {@code true}，InputStream 不能被多次读取，并且必须在读取后及时关闭以避免资源泄漏。
     * 对于典型的资源描述符，这将是 {@code false}。
     */
    default boolean isOpen() {
        return false;
    }

    /**
     * 判断此资源是否代表文件系统中的一个文件。
     * {@code true} 值强烈暗示（但不保证）{@link #getFile()} 调用会成功。
     * 默认情况下，这是保守地设置为 {@code false}。
     */
    default boolean isFile() {
        return false;
    }

    /**
     * 返回此资源的 URL 句柄。
     * @throws IOException 如果资源无法作为 URL 解析，即如果资源作为描述符不可用。
     */
    URL getURL() throws IOException;

    /**
     * 返回此资源的 URI 句柄。
     * @throws IOException 如果资源无法作为 URI 解析，即如果资源作为描述符不可用。
     */
    URI getURI() throws IOException;

    /**
     * 返回此资源的文件句柄。
     * @throws java.io.FileNotFoundException 如果资源无法解析为绝对文件路径，即如果资源在文件系统中不可用。
     * @throws IOException 如果在解析/读取时发生一般性故障。
     */
    File getFile() throws IOException;

    /**
     * 返回一个 {@link ReadableByteChannel}。
     * 预期每次调用都会创建一个新的通道。
     * 默认实现通过 {@link #getInputStream()} 的结果返回 {@link Channels#newChannel(InputStream)}。
     * @return 此资源底层的字节通道（不能为空）。
     * @throws java.io.FileNotFoundException 如果底层资源不存在。
     * @throws IOException 如果无法打开内容通道。
     */
    default ReadableByteChannel readableChannel() throws IOException {
        return Channels.newChannel(getInputStream());
    }

    /**
     * 将此资源的内容作为字节数组返回。
     * @return 此资源的内容作为字节数组。
     * @throws java.io.FileNotFoundException 如果资源无法解析为绝对文件路径，即如果资源在文件系统中不可用。
     * @throws IOException 如果在解析/读取时发生一般性故障。
     */
    default byte[] getContentAsByteArray() throws IOException {
        return FileCopyUtils.copyToByteArray(getInputStream());
    }

    /**
     * 使用指定的字符集，将此资源的内容作为字符串返回。
     * @param charset 用于解码的字符集。
     * @return 此资源的内容作为一个 {@code String}。
     * @throws java.io.FileNotFoundException 如果资源无法解析为绝对文件路径，即如果资源在文件系统中不可用。
     * @throws IOException 如果在解析/读取时发生一般性故障。
     */
    default String getContentAsString(Charset charset) throws IOException {
        return FileCopyUtils.copyToString(new InputStreamReader(getInputStream(), charset));
    }

    /**
     * 确定此资源的内容长度。
     * @throws IOException 如果无法解析资源（在文件系统或作为其他已知的物理资源类型）。
     */
    long contentLength() throws IOException;

    /**
     * 确定此资源最后修改的时间戳。
     * @throws IOException 如果无法解析资源（在文件系统或作为其他已知的物理资源类型）。
     */
    long lastModified() throws IOException;

    /**
     * 创建相对于此资源的相对路径资源。
     * @param relativePath 相对于此资源的相对路径。
     * @return 相对资源的资源句柄。
     * @throws IOException 如果无法确定相对资源。
     */
    Resource createRelative(String relativePath) throws IOException;

    /**
     * 确定此资源的文件名 - 通常是路径的最后一部分 - 例如，{@code "myfile.txt"}。
     * 如果此类资源没有文件名，则返回 {@code null}。
     * 鼓励实现返回未编码的文件名。
     */
    @Nullable
    String getFilename();

    /**
     * 返回此资源的描述，用于在使用资源时输出错误信息。
     * 鼓励实现也从他们的 {@code toString} 方法返回这个值。
     * @see Object#toString()
     */
    String getDescription();

}


